import type { Metadata } from "next" import Link from "next/link" import { createSupabaseAdminClient } from "@/lib/supabase/admin" import { sanitizeEntryContent } from "@/lib/sanitize" import { SharedEntryContent } from "./shared-entry-content" interface SharedPageProperties { params: Promise<{ token: string }> } interface SharedHighlightData { highlightedText: string textOffset: number textLength: number textPrefix: string textSuffix: string } interface SharedEntryRow { id: string entry_id: string expires_at: string | null note: string | null note_is_public: boolean view_count: number expiry_interval_days: number highlighted_text: string | null highlight_text_offset: number | null highlight_text_length: number | null highlight_text_prefix: string | null highlight_text_suffix: string | null entries: { id: string title: string | null url: string | null author: string | null summary: string | null content_html: string | null published_at: string | null enclosure_url: string | null feeds: { title: string | null } } } async function fetchSharedEntry(token: string) { const adminClient = createSupabaseAdminClient() const { data, error } = await adminClient .from("shared_entries") .select( "id, entry_id, expires_at, note, note_is_public, view_count, expiry_interval_days, highlighted_text, highlight_text_offset, highlight_text_length, highlight_text_prefix, highlight_text_suffix, entries!inner(id, title, url, author, summary, content_html, published_at, enclosure_url, feeds!inner(title))" ) .eq("share_token", token) .maybeSingle() if (error || !data) return null const row = data as unknown as SharedEntryRow if (row.expires_at && new Date(row.expires_at) < new Date()) { return { expired: true as const } } let highlightData: SharedHighlightData | null = null if ( row.highlighted_text && row.highlight_text_offset !== null && row.highlight_text_length !== null ) { highlightData = { highlightedText: row.highlighted_text, textOffset: row.highlight_text_offset, textLength: row.highlight_text_length, textPrefix: row.highlight_text_prefix ?? "", textSuffix: row.highlight_text_suffix ?? "", } } const publicNote = row.note_is_public && row.note ? row.note : null const expiryIntervalDays = row.expiry_interval_days ?? 7 const newExpiresAt = new Date( Date.now() + expiryIntervalDays * 24 * 60 * 60 * 1000 ).toISOString() adminClient .from("shared_entries") .update({ view_count: row.view_count + 1, last_viewed_at: new Date().toISOString(), expires_at: newExpiresAt, }) .eq("id", row.id) .then(() => {}) return { expired: false as const, entry: row.entries, highlightData, publicNote } } export async function generateMetadata({ params, }: SharedPageProperties): Promise { const { token } = await params const result = await fetchSharedEntry(token) if (!result || result.expired) { return { title: "shared entry — asa.news" } } return { title: `${result.entry.title ?? "untitled"} — asa.news`, description: result.entry.summary?.slice(0, 200) ?? undefined, openGraph: { title: result.entry.title ?? "shared entry", description: result.entry.summary?.slice(0, 200) ?? undefined, siteName: "asa.news", }, } } export default async function SharedPage({ params }: SharedPageProperties) { const { token } = await params const result = await fetchSharedEntry(token) if (!result) { return (

shared entry not found

this shared link is no longer available or has been removed.

) } if (result.expired) { return (

this share has expired

shared links expire after a set period. the owner may share it again if needed.

) } const entry = result.entry const sanitisedHtml = sanitizeEntryContent(entry.content_html || entry.summary || "") const formattedDate = entry.published_at ? new Date(entry.published_at).toLocaleDateString("en-GB", { day: "numeric", month: "long", year: "numeric", }) : null return (

{entry.title}

{entry.feeds?.title && {entry.feeds.title}} {entry.author && · {entry.author}} {formattedDate && · {formattedDate}}
{result.publicNote && (
{result.publicNote}
)} {entry.enclosure_url && (
)}
) }